
/**
 * 初始化块
 * <p>
 * Java使用构造器来对单个对象进行初始化操作，使用构造器先完成整个Java对象的状态初始化，然后将Java对象返回给程序，从而让该Java对象的信息更加完整。
 * 与构造器作用非常类似的是初始化块，它也可以对Java对象进行初始化操作。
 * <p>
 * 一个类里可以有多个初始化块，相同类型的初始化块之间有顺序：前面定义的初始化块先执行，后面定义的初始化块后执行。
 * 初始化块的语法格式如下：
 * <pre>
 *     [修饰符] {
 *         // 初始化块的可执行代码
 *         ...
 *     }
 * </pre>
 * 初始化块的修饰符只能是static，使用static修饰的初始化块被称为类初始化块（静态初始化块），没有static修饰的初始化块被称为实例初始化块（非静态初始化块）。
 * 初始化块里的代码可以包含任何可执行性语句，包括定义局部变量、调用其他对象的方法，以及使用分支、循环语句等。
 */

class Person {
    // 下面定义一个实例初始化块
    {
        int a = 6;
        if (a > 4) {
            System.out.println("Person实例初始化块：局部变量a的值大于4");
        }
        System.out.println("Person的实例初始化块");
    }

    // 定义第二个实例初始化块
    {
        System.out.println("Person的第二个实例初始化块");
    }

    // 定义无参数的构造器
    public Person() {
        System.out.println("Person类的无参数构造器");
    }

    public static void main() {
        new Person();
    }
}

/**
 * 实例初始化块只在创建Java对象时隐式执行，而且在构造器执行之前自动执行。
 * <p>
 * 实例初始化块和构造器的作用非常相似，但也有不同之处：
 * 实例初始化块、声明实例变量指定的默认值都可认为是对象的初始化代码，它们的执行顺序与源程序中的排列顺序相同。
 */
class InstanceInitTest {
    // 先执行实例初始化块将a实例变量赋值为6
    {
        a = 6;
    }

    // 再执行将a实例变量赋值为9
    int a = 9;

    public static void main() {
        // 下面代码将输出9
        System.out.println(new InstanceInitTest().a);
    }
}

/**
 * 注意：当Java创建一个对象时，系统先为该对象的所有实例变量分配内存（前提是该类已经被加载过了），
 * 接着程序开始对这些实例变量执行初始化，其初始化顺序是：
 * 先执行实例初始化块或声明实例变量时指定的初始值（这两个地方指定初始值的执行顺序与它们在源代码中的排列顺序相同），
 * 再执行构造器里指定的初始值。
 * <p>
 * 实例初始化块和构造器
 * <p>
 * 从某种程度上来看，实例初始化块是构造器的补充，实例初始化块总是在构造器执行之前执行。系统同样可使用实例初始化块来进行对象的初始化操作。
 * 如果有一段初始化处理代码对所有对象完全相同，且无须接收任何参数，就可以把这段初始化处理代码提取到实例初始化块中。
 * <p>
 * 注意：实际上实例初始化块是一个假象，使用javac命令编译Java类后，该Java类中的实例初始化块会消失—实例初始化块中代码会被“还原”到每个构造器中，且位于构造器所有代码的前面。
 * <p>
 * 与构造器类似，实例化块也是从父类开始向下依次执行：
 * 父类初始化块-》父类构造器-》子类初始化块-》子类构造器
 * <p>
 * 实例初始化块和构造器
 * <p>
 * 从某种程度上来看，实例初始化块是构造器的补充，实例初始化块总是在构造器执行之前执行。系统同样可使用实例初始化块来进行对象的初始化操作。
 * 如果有一段初始化处理代码对所有对象完全相同，且无须接收任何参数，就可以把这段初始化处理代码提取到实例初始化块中。
 * <p>
 * 注意：实际上实例初始化块是一个假象，使用javac命令编译Java类后，该Java类中的实例初始化块会消失—实例初始化块中代码会被“还原”到每个构造器中，且位于构造器所有代码的前面。
 * <p>
 * 与构造器类似，实例化块也是从父类开始向下依次执行：
 * 父类初始化块-》父类构造器-》子类初始化块-》子类构造器
 * <p>
 * 实例初始化块和构造器
 * <p>
 * 从某种程度上来看，实例初始化块是构造器的补充，实例初始化块总是在构造器执行之前执行。系统同样可使用实例初始化块来进行对象的初始化操作。
 * 如果有一段初始化处理代码对所有对象完全相同，且无须接收任何参数，就可以把这段初始化处理代码提取到实例初始化块中。
 * <p>
 * 注意：实际上实例初始化块是一个假象，使用javac命令编译Java类后，
 * 该Java类中的实例初始化块会消失—实例初始化块中代码会被“还原”到每个构造器中，且位于构造器所有代码的前面。
 * <p>
 * 与构造器类似，实例化块也是从父类开始向下依次执行：
 * 父类初始化块-》父类构造器-》子类初始化块-》子类构造器
 * <p>
 * 实例初始化块和构造器
 * <p>
 * 从某种程度上来看，实例初始化块是构造器的补充，实例初始化块总是在构造器执行之前执行。系统同样可使用实例初始化块来进行对象的初始化操作。
 * 如果有一段初始化处理代码对所有对象完全相同，且无须接收任何参数，就可以把这段初始化处理代码提取到实例初始化块中。
 * <p>
 * 注意：实际上实例初始化块是一个假象，使用javac命令编译Java类后，
 * 该Java类中的实例初始化块会消失—实例初始化块中代码会被“还原”到每个构造器中，且位于构造器所有代码的前面。
 * <p>
 * 与构造器类似，实例化块也是从父类开始向下依次执行：
 * 父类初始化块-》父类构造器-》子类初始化块-》子类构造器
 * <p>
 * 实例初始化块和构造器
 * <p>
 * 从某种程度上来看，实例初始化块是构造器的补充，实例初始化块总是在构造器执行之前执行。系统同样可使用实例初始化块来进行对象的初始化操作。
 * 如果有一段初始化处理代码对所有对象完全相同，且无须接收任何参数，就可以把这段初始化处理代码提取到实例初始化块中。
 * <p>
 * 注意：实际上实例初始化块是一个假象，使用javac命令编译Java类后，
 * 该Java类中的实例初始化块会消失—实例初始化块中代码会被“还原”到每个构造器中，且位于构造器所有代码的前面。
 * <p>
 * 与构造器类似，实例化块也是从父类开始向下依次执行：
 * 父类初始化块-》父类构造器-》子类初始化块-》子类构造器
 * <p>
 * 实例初始化块和构造器
 * <p>
 * 从某种程度上来看，实例初始化块是构造器的补充，实例初始化块总是在构造器执行之前执行。系统同样可使用实例初始化块来进行对象的初始化操作。
 * 如果有一段初始化处理代码对所有对象完全相同，且无须接收任何参数，就可以把这段初始化处理代码提取到实例初始化块中。
 * <p>
 * 注意：实际上实例初始化块是一个假象，使用javac命令编译Java类后，
 * 该Java类中的实例初始化块会消失—实例初始化块中代码会被“还原”到每个构造器中，且位于构造器所有代码的前面。
 * <p>
 * 与构造器类似，实例化块也是从父类开始向下依次执行：
 * 父类初始化块-》父类构造器-》子类初始化块-》子类构造器
 * <p>
 * 实例初始化块和构造器
 * <p>
 * 从某种程度上来看，实例初始化块是构造器的补充，实例初始化块总是在构造器执行之前执行。系统同样可使用实例初始化块来进行对象的初始化操作。
 * 如果有一段初始化处理代码对所有对象完全相同，且无须接收任何参数，就可以把这段初始化处理代码提取到实例初始化块中。
 * <p>
 * 注意：实际上实例初始化块是一个假象，使用javac命令编译Java类后，
 * 该Java类中的实例初始化块会消失—实例初始化块中代码会被“还原”到每个构造器中，且位于构造器所有代码的前面。
 * <p>
 * 与构造器类似，实例化块也是从父类开始向下依次执行：
 * 父类初始化块-》父类构造器-》子类初始化块-》子类构造器
 * <p>
 * 实例初始化块和构造器
 * <p>
 * 从某种程度上来看，实例初始化块是构造器的补充，实例初始化块总是在构造器执行之前执行。系统同样可使用实例初始化块来进行对象的初始化操作。
 * 如果有一段初始化处理代码对所有对象完全相同，且无须接收任何参数，就可以把这段初始化处理代码提取到实例初始化块中。
 * <p>
 * 注意：实际上实例初始化块是一个假象，使用javac命令编译Java类后，
 * 该Java类中的实例初始化块会消失—实例初始化块中代码会被“还原”到每个构造器中，且位于构造器所有代码的前面。
 * <p>
 * 与构造器类似，实例化块也是从父类开始向下依次执行：
 * 父类初始化块-》父类构造器-》子类初始化块-》子类构造器
 */

/**
 * 实例初始化块和构造器
 *
 * 从某种程度上来看，实例初始化块是构造器的补充，实例初始化块总是在构造器执行之前执行。系统同样可使用实例初始化块来进行对象的初始化操作。
 * 如果有一段初始化处理代码对所有对象完全相同，且无须接收任何参数，就可以把这段初始化处理代码提取到实例初始化块中。
 *
 * 注意：实际上实例初始化块是一个假象，使用javac命令编译Java类后，
 * 该Java类中的实例初始化块会消失—实例初始化块中代码会被“还原”到每个构造器中，且位于构造器所有代码的前面。
 *
 * 与构造器类似，实例化块也是从父类开始向下依次执行：
 * 父类初始化块-》父类构造器-》子类初始化块-》子类构造器
 */

/**
 * 类初始化块
 *
 * 定义初始化块时使用static修饰符,也被称为静态初始化块
 * 类的初始化过程：父类的类初始化块-》子类的类初始化块
 */
class Root {
    static {
        System.out.println("Root的类初始化块");
    }

    {
        System.out.println("Root的实例初始化块");
    }

    public Root() {
        System.out.println("Root的无参数的构造器");
    }
}

class Mid extends Root {
    static {
        System.out.println("Mid的类初始化块");
    }

    {
        System.out.println("Mid的实例初始化块");
    }

    public Mid() {
        System.out.println("Mid的无参数的构造器");
    }

    public Mid(String msg) {
        this();
        System.out.println("Mid的带参数构造器，其参数值：" + msg);
    }
}

class Leaf extends Mid {
    static {
        System.out.println("Leaf的类初始化块");
    }

    {
        System.out.println("Leaf的实例初始化块");
    }

    public Leaf() {
        super("Java初始化块");
        System.out.println("执行Leaf的构造器");
    }
}

/**
 * 注意：Java系统加载并初始化某个类时，总是保证该类的所有父类（包括直接父类和间接父类）全部加载并初始化。
 *
 * 类初始化块和声明类变量时所指定的初始值都是该类的初始化代码，它们的执行顺序与源程序中的排列顺序相同。
 */
class StaticInitTest {
    // 先执行类初始化块将类变量a赋值为6
    static {
        a = 6;
    }

    // 再将类变量a赋值为9
    static int a = 9;

    public static void main() {
        // 下面代码将输出9
        System.out.println(StaticInitTest.a);
    }
}

/**
 * 提示：当JVM第一次主动使用某个类时，系统会在类准备阶段为该类的所有类变量分配内存；
 * 在初始化阶段则负责初始化这些类变量，初始化类变量就是执行类初始化块代码和声明类成员变量时指定的初始值，
 * 它们的执行顺序与源代码中的排列顺序相同。
 *
 * 通过查看.class文件，无法得知，是先执行类初始化块还是先执行类变量赋值（因为.class文件中，类初始化块总是在最下面，
 * 无法通过顺序判断谁先执行）
 */

public class InitBlock {
    public static void leafTest() {
        new Leaf();
        new Leaf();
    }

    public static void main(String[] args) {
        Person.main();
        InstanceInitTest.main();
        leafTest();
        StaticInitTest.main();
    }
}
